package org.jnarcissus;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.CoreMatchers.sameInstance;

import org.hamcrest.Matcher;
import org.jbrisk.tests.IExceptionAssertRunner;

public class JNarcissus {

	private static JNarcissusCore coreInstance = new JNarcissusCore();

	public static <T> MonitoredInstanceData assertEquals(String reason, T expectedValue, T methodCall) {

		return coreInstance.assertThat(reason, methodCall, equalTo(expectedValue));
	}

	public static <T> MonitoredInstanceData assertEquals(T expectedValue, T methodCall) {

		return assertEquals(null, expectedValue, methodCall);
	}

	public static MonitoredInstanceData assertFalse(Boolean methodCall) {

		return assertFalse(null, methodCall);
	}

	public static MonitoredInstanceData assertFalse(String reason, Boolean methodCall) {

		return coreInstance.assertThat(reason, methodCall, equalTo(false));
	}

	public static void assertInstance(IMonitoredInstance instanceToAssert) {

		coreInstance.assertInstance(instanceToAssert);
	}

	public static void assertInstance(Object instanceToAssert) {

		coreInstance.assertInstance(instanceToAssert);
	}

	public static <T> MonitoredInstanceData assertNotEquals(String reason, T expectedValue, T methodCall) {

		return coreInstance.assertThat(reason, methodCall, not(equalTo(expectedValue)));
	}

	public static <T> MonitoredInstanceData assertNotEquals(T expectedValue, T methodCall) {

		return assertNotEquals(null, expectedValue, methodCall);
	}

	public static <T> MonitoredInstanceData assertNotNull(String reason, T methodCall) {

		return coreInstance.assertThat(reason, methodCall, not(nullValue()));
	}

	public static <T> MonitoredInstanceData assertNotNull(T methodCall) {

		return assertNotNull(null, methodCall);
	}

	public static <T> MonitoredInstanceData assertNull(String reason, T methodCall) {

		return coreInstance.assertThat(reason, methodCall, nullValue());
	}

	public static <T> MonitoredInstanceData assertNull(T methodCall) {

		return assertNull(null, methodCall);
	}

	public static <T> MonitoredInstanceData assertSame(String reason, T expectedValue, T methodCall) {

		return coreInstance.assertThat(reason, methodCall, sameInstance(expectedValue));
	}

	public static <T> MonitoredInstanceData assertSame(T expectedValue, T methodCall) {

		return assertSame(null, expectedValue, methodCall);
	}

	public static <T> MonitoredInstanceData assertThat(T methodCall, Matcher<T> matcher) {

		return coreInstance.assertThat(methodCall, matcher);
	}

	public static <T> T assertMethod(T monitoredInstance, MonitoredInstanceMethodMatcher matcher) {
	
		return coreInstance.assertMethod(monitoredInstance, matcher);
	}
	
	public static <T extends Throwable> MonitoredInstanceData assertThatThrows(Class<T> expectedExceptionClass, IExceptionAssertRunner runner) {

		return assertThatThrows(null, expectedExceptionClass, runner);
	}

	public static <T extends Throwable> MonitoredInstanceData assertThatThrows(String expectedExceptionMessage, Class<T> expectedExceptionClass,
			IExceptionAssertRunner runner) {

		return coreInstance.assertThatThrows(expectedExceptionMessage, expectedExceptionClass, runner);
	}

	public static MonitoredInstanceData assertTrue(Boolean methodCall) {

		return assertTrue(null, methodCall);
	}

	public static MonitoredInstanceData assertTrue(String reason, Boolean methodCall) {

		return coreInstance.assertThat(reason, methodCall, equalTo(true));
	}

	/**
	 * Creates an instance from the supplied class using the no-arguments
	 * constructor. If you need to supply arguments, check the
	 * {@link JNarcissusCore#create(Class, Object[])} and
	 * {@link JNarcissusCore#create(Class, Class[], Object[])} overloads.
	 * 
	 * @param <T>
	 *            Class type.
	 * @param classToCreateFrom
	 *            The class to create the instance from.
	 * @return Created instance.
	 * 
	 */
	public static <T> T create(Class<T> classToCreateFrom) {

		return coreInstance.create(classToCreateFrom, null, null);
	}

	/**
	 * Creates an instance from the supplied class with the supplied constructor
	 * arguments.
	 * 
	 * @param <T>
	 *            Class type.
	 * @param classToCreateFrom
	 *            The class to create the instance from.
	 * @param constructorArgsTypes
	 *            List of ordered argument types. It will use this value to find
	 *            the proper constructor.
	 * @param constructorArgs
	 *            List of arguments to supply to the class constructor.
	 * @return Created instance.
	 */
	public static <T> T create(Class<T> classToCreateFrom, Class<?>[] constructorArgsTypes, Object[] constructorArgs) {

		return coreInstance.create(classToCreateFrom, constructorArgsTypes, constructorArgs);
	}

	/**
	 * Creates an instance from the supplied class with the supplied constructor
	 * arguments. It will try to find the correct constructor by the ordered
	 * array of argument values. If it can't (maybe because of a null value or
	 * because of boxed arguments), use the
	 * {@link JNarcissusCore#create(Class, Class[], Object[])} overload.
	 * 
	 * @param <T>
	 *            Class type.
	 * @param classToCreateFrom
	 *            The class to create the instance from.
	 * @param constructorArgs
	 *            List of arguments to supply to the class constructor.
	 * @return Created instance.
	 */
	public static <T> T create(Class<T> classToCreateFrom, Object[] constructorArgs) {

		return coreInstance.create(classToCreateFrom, null, constructorArgs);
	}

	public static <T> void removeAssert(T methodCall) {

		coreInstance.removeAssert(methodCall);
	}

	public static void reset() {

		coreInstance.reset();
	}
}
